import snscrape.modules.twitter as sntwitter
ob = sntwitter.TwitterSearchScraper("from:narendramodi (filter:safe OR -filter:safe)")
i.lang
'en'
import numpy as np
import pandas as pd
import os
# Pre porcessing
import re # Regular expression
import nltk
nltk.download("stopwords")
from nltk.corpus import stopwords
from nltk.stem.porter import *
#For building the model
from sklearn.model_selection import train_test_split
import tensorflow as tf
import seaborn as sns
#For data visulisation
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
pd.options.plotting.backend = "plotly"
[nltk_data] Downloading package stopwords to [nltk_data] C:\Users\Asus\AppData\Roaming\nltk_data... [nltk_data] Package stopwords is already up-to-date!
df1 = pd.read_csv("C:\\Users\\Asus\\Downloads\\Twitter_Data.csv")
df1.shape
(162980, 2)
df2 = pd.read_csv("C:\\Users\\Asus\\Downloads\\apple-twitter-sentiment-texts.csv")
df2 = df2.rename(columns = {'text':'clean_text','sentiment':'category'})
df2['category'] = df2['category'].map({-1:-1.0,0:0,1:1.0})
df2.head()
| clean_text | category | |
|---|---|---|
| 0 | Wow. Yall needa step it up @Apple RT @heynyla:... | -1.0 |
| 1 | What Happened To Apple Inc? http://t.co/FJEX... | 0.0 |
| 2 | Thank u @apple I can now compile all of the pi... | 1.0 |
| 3 | The oddly uplifting story of the Apple co-foun... | 0.0 |
| 4 | @apple can i exchange my iphone for a differen... | 0.0 |
df3 = pd.read_csv("C:\\Users\\Asus\\Downloads\\finalSentimentdata2.csv")
df3 = df3.rename(columns = {'text':'clean_text','sentiment':'category'})
df3['category'] = df3['category'].map({'sad':-1.0,'anger':-1.0,'fear':-1.0,'joy':1.0})
df3 = df3.drop(['Unnamed: 0'],axis = 1)
df3.head()
| category | clean_text | |
|---|---|---|
| 0 | -1.0 | agree the poor in india are treated badly thei... |
| 1 | 1.0 | if only i could have spent the with this cutie... |
| 2 | 1.0 | will nature conservation remain a priority in ... |
| 3 | -1.0 | coronavirus disappearing in italy show this to... |
| 4 | -1.0 | uk records lowest daily virus death toll since... |
df4 = pd.read_csv("C:\\Users\\Asus\\Downloads\\Tweets.csv")
df4 = df4.rename(columns = {'text':'clean_text','airline_sentiment':'category'})
df4["category"] = df4['category'].map({'negative':-1.0,'neutral':0.0,'positive':1.0})
df4 = df4[['category','clean_text']]
df4.head()
| category | clean_text | |
|---|---|---|
| 0 | 0.0 | @VirginAmerica What @dhepburn said. |
| 1 | 1.0 | @VirginAmerica plus you've added commercials t... |
| 2 | 0.0 | @VirginAmerica I didn't today... Must mean I n... |
| 3 | -1.0 | @VirginAmerica it's really aggressive to blast... |
| 4 | -1.0 | @VirginAmerica and it's a really big bad thing... |
df = pd.concat([df1,df2,df3,df4],ignore_index = True)
df.isnull().sum()
clean_text 4 category 7 dtype: int64
df.shape
(182340, 2)
df.dropna(axis =0,inplace =True)
df.category = df['category'].map({-1.0:'Negative',0.0:'Neutral',1.0:'Positive'})
df.head()
| clean_text | category | |
|---|---|---|
| 0 | when modi promised “minimum government maximum... | Negative |
| 1 | talk all the nonsense and continue all the dra... | Neutral |
| 2 | what did just say vote for modi welcome bjp t... | Positive |
| 3 | asking his supporters prefix chowkidar their n... | Positive |
| 4 | answer who among these the most powerful world... | Positive |
df.groupby('category').count().plot(kind = 'bar')
# calculate the tweet lengths
tweet_len = pd.Series([len(i.split()) for i in df.clean_text])
tweet_len.plot(kind = 'box')
# for positive sentiment analysis
fig = plt.figure(figsize=(14,7))
df['length'] = df.clean_text.str.split().apply(len)
ax1 = fig.add_subplot(122)
sns.histplot(df[df['category'] == 'Positive']['length'],ax = ax1,color = 'green')
describe = df.length[df.category == 'Positive'].describe().to_frame().round(2)
ax2 = fig.add_subplot(121)
ax2.axis('off')
font_size = 14
bbox = [0,0,1,1]
table = ax2.table(cellText = describe.values, rowLabels = describe.index,bbox = bbox,colLabels = describe.columns)
table.set_fontsize(font_size)
fig.suptitle('Distribution of text length for positive sentiment tweets.',fontsize = 16)
plt.show()
fig = plt.figure(figsize = (14,7))
ax1 = fig.add_subplot(122)
sns.histplot(df[df['category'] == 'Negative']['length'],ax = ax1,color = 'red')
describe = df.length[df['category'] == 'Negative'].describe().to_frame().round(2)
ax2 = fig.add_subplot(121)
ax2.axis('off')
font_size = 14
bbox = [0,0,1,1]
table = ax2.table(cellText = describe.values,rowLabels = describe.index,bbox = bbox, colLabels = describe.columns)
table.set_fontsize(font_size)
fig.suptitle('Distribution of text length for Negative sentiment tweets.', fontsize=16)
plt.show()
import plotly.express as px
fig = px.pie(df,names='category',title = 'Pie Chart of different sentiment of tweets')
fig.show()
df.drop(['length'],axis = 1,inplace = True)
df.head()
| clean_text | category | |
|---|---|---|
| 0 | when modi promised “minimum government maximum... | Negative |
| 1 | talk all the nonsense and continue all the dra... | Neutral |
| 2 | what did just say vote for modi welcome bjp t... | Positive |
| 3 | asking his supporters prefix chowkidar their n... | Positive |
| 4 | answer who among these the most powerful world... | Positive |
from wordcloud import WordCloud, STOPWORDS
def wordcount_gen(df,category):
'''
Generating Word Cloud
inputs:
-df: tweets dataset
-category: Positive/Negative/Neutral'''
# combine all tweets
combined_tweets = ' '.join([tweet for tweet in df[df.category == category]['clean_text']])
# Initialize wordcloud object
wc = WordCloud(background_color = 'white',
max_words = 50,
stopwords = STOPWORDS)
# Generate and plot wordcloud
plt.figure(figsize=(10,10))
plt.imshow(wc.generate(combined_tweets))
plt.title('{} Sentiment words'.format(category),fontsize = 20)
plt.axis('off')
plt.show()
wordcount_gen(df,'Negative')
wordcount_gen(df,'Positive')
wordcount_gen(df,'Neutral')
def tweet_to_word(tweet):
text = tweet.lower()
text = re.sub(r'[^a-zA-Z0-9]',' ',text)
words = text.split()
words = [w for w in words if w not in stopwords.words("english")]
words = [PorterStemmer().stem(w) for w in words]
return words
print("\n Original tweet ->",df['clean_text'][0])
print("\n Processed tweet ->",tweet_to_word(df['clean_text'][0]))
Original tweet -> when modi promised “minimum government maximum governance” expected him begin the difficult job reforming the state why does take years get justice state should and not business and should exit psus and temples Processed tweet -> ['modi', 'promis', 'minimum', 'govern', 'maximum', 'govern', 'expect', 'begin', 'difficult', 'job', 'reform', 'state', 'take', 'year', 'get', 'justic', 'state', 'busi', 'exit', 'psu', 'templ']
X = list(map(tweet_to_word,df['clean_text']))
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
Y = le.fit_transform(df['category'])
print(X[0])
print(Y[0])
['modi', 'promis', 'minimum', 'govern', 'maximum', 'govern', 'expect', 'begin', 'difficult', 'job', 'reform', 'state', 'take', 'year', 'get', 'justic', 'state', 'busi', 'exit', 'psu', 'templ'] 0
y = pd.get_dummies(df['category'])
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.2,random_state = 1)
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=1)
y_train
| Negative | Neutral | Positive | |
|---|---|---|---|
| 45085 | 0 | 1 | 0 |
| 139119 | 0 | 0 | 1 |
| 45560 | 0 | 1 | 0 |
| 2517 | 1 | 0 | 0 |
| 76774 | 0 | 0 | 1 |
| ... | ... | ... | ... |
| 136078 | 1 | 0 | 0 |
| 39149 | 0 | 0 | 1 |
| 41165 | 0 | 0 | 1 |
| 110048 | 1 | 0 | 0 |
| 46696 | 0 | 1 | 0 |
109397 rows × 3 columns
from sklearn.feature_extraction.text import CountVectorizer
vocabular_size = 5000
count_vector = CountVectorizer(max_features = vocabular_size,preprocessor = lambda x:x,tokenizer = lambda x:x)
X_train = count_vector.fit_transform(X_train).toarray()
X_train = count_vector.transform(X_test).toarray()
C:\Users\Asus\anaconda3\envs\sentimentanalysis\lib\site-packages\sklearn\feature_extraction\text.py:528: UserWarning: The parameter 'token_pattern' will not be used since 'tokenizer' is not None'
print(count_vector.get_feature_names_out()[0:200])
['0' '000' '1' '10' '100' '1000' '10000' '10th' '11' '111' '1145' '11th' '12' '1200' '12000' '125' '12k' '130' '140' '14000' '15' '150' '1500' '1500000' '1520' '157200000' '15l' '15lac' '15lakh' '18' '180' '19' '1947' '1958' '1962' '1969' '1971' '1980' '1984' '1998' '1st' '2' '20' '200' '2000' '2002' '2004' '2007' '2008' '2009' '2010' '2011' '2012' '2013' '2014' '2015' '2016' '2017' '2018' '2019' '2020' '2022' '2024' '2029' '21st' '23' '23rd' '24' '247' '24x7' '25' '250' '2611' '26th' '272' '27th' '280319' '282' '28th' '2cr' '2day' '2nd' '3' '30' '300' '3000' '30000' '300km' '31st' '350' '35a' '370' '3rd' '4' '40' '400' '4000' '45' '456' '4th' '5' '50' '500' '5000' '50000' '5th' '5year' '5yr' '6' '60' '600' '6000' '6th' '7' '72000' '7200000' '72k' '7th' '8' '80' '800' '8020' '9' '90' '9000' 'aa' 'aadhaar' 'aadhar' 'aadmi' 'aag' 'aaj' 'aalo' 'aam' 'aan' 'aap' 'aapk' 'aapko' 'aapl' 'aapn' 'aay' 'aaya' 'aayega' 'aayog' 'abandon' 'abdul' 'abdullah' 'abe' 'abhi' 'abhinandan' 'abhisar' 'abhiyan' 'abil' 'abki' 'abl' 'abolish' 'abroad' 'abscond' 'absolut' 'absurd' 'abt' 'abus' 'abv' 'academ' 'acc' 'accept' 'access' 'acch' 'accha' 'accid' 'accident' 'accommod' 'accompani' 'accomplish' 'accord' 'accordingli' 'account' 'accumul' 'accur' 'accus' 'ach' 'acha' 'acheiv' 'achh' 'achiev' 'achiv' 'acknowledg' 'acquir' 'acquit' 'acronym' 'across' 'act' 'action' 'activ' 'activist' 'actor' 'actress' 'actual' 'ad' 'adani' 'add']
X_train
array([[30, 0, 0, ..., 0, 0, 0],
[27, 1, 0, ..., 0, 0, 0],
[26, 0, 3, ..., 0, 0, 0],
...,
[28, 0, 1, ..., 0, 0, 0],
[10, 1, 2, ..., 0, 0, 0],
[29, 1, 1, ..., 0, 0, 0]], dtype=int64)
plt.plot(X_train[2,:])
plt.xlabel('Word')
plt.ylabel('Count')
plt.show()
from keras.preprocessing.text import Tokenizer
from keras_preprocessing.sequence import pad_sequences
max_words = 5000
max_len = 50
def tokenizer_pad_sequences(text):
tokenizer = Tokenizer(num_words=max_words,lower=True, split = ' ')
tokenizer.fit_on_texts(text)
X = tokenizer.texts_to_sequences(text)
X = pad_sequences(X,padding ='post',maxlen = max_len)
return X, tokenizer
print('Before Tokenization & Padding \n', df['clean_text'][0])
X, tokenizer = tokenizer_pad_sequences(df['clean_text'])
print('After Tokenization & Padding \n',X[0])
Before Tokenization & Padding
when modi promised “minimum government maximum governance” expected him begin the difficult job reforming the state why does take years get justice state should and not business and should exit psus and temples
After Tokenization & Padding
[ 41 1 349 73 1911 1180 44 2465 2 1259 219 2 236 32
165 102 53 55 1184 236 50 3 6 533 3 50 3833 3
3077 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0]
import pickle
with open('tokenizer.pickle','wb') as handle:
pickle.dump(tokenizer,handle,protocol = pickle.HIGHEST_PROTOCOL)
with open('tokenizer.pickle','rb') as handle:
tokenizer = pickle.load(handle)
y = pd.get_dummies(df['category'])
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,random_state=1)
X_train, X_val, y_train, y_val = train_test_split(X_train,y_train,test_size=0.2,random_state=1)
print('Train Set ->', X_train.shape, y_val.shape)
print('Validation Set ->', X_val.shape, y_val.shape)
print('Test Set ->', X_test.shape, y_test.shape)
Train Set -> (116690, 50) (29173, 3) Validation Set -> (29173, 50) (29173, 3) Test Set -> (36466, 50) (36466, 3)
import keras.backend as K
def f1_score(precision, recall):
f1_val = 2*(precision*recall)/(precision+recall+K.epsilon())
return f1_val
from keras.models import Sequential
from keras.layers import Embedding, Conv1D,MaxPooling1D,Bidirectional,LSTM,Dense,Dropout
from keras.metrics import Precision, Recall
from keras.optimizers import SGD
from keras.optimizers import RMSprop
from keras import datasets
from keras.callbacks import LearningRateScheduler
from keras.callbacks import History
from keras import losses
vocab_size = 5000
embedding_size = 32
epochs=20
learning_rate = 0.1
decay_rate = learning_rate / epochs
momentum = 0.8
sgd = SGD(learning_rate=learning_rate, momentum=momentum, decay=decay_rate, nesterov=False)
model= Sequential()
model.add(Embedding(vocab_size, embedding_size, input_length=max_len))
model.add(Conv1D(filters=32, kernel_size=3, padding='same', activation='relu'))
model.add(MaxPooling1D(pool_size=2))
model.add(Bidirectional(LSTM(32)))
model.add(Dropout(0.4))
model.add(Dense(3, activation='softmax'))
print(model.summary())
# Complie model
model.compile(loss='categorical_crossentropy',optimizer=sgd,metrics=['accuracy',Precision(),Recall()])
# Train model
batch_size = 64
history = model.fit(X_train,y_train,validation_data=(X_val,y_val),
batch_size = batch_size, epochs = epochs, verbose = 1)
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_1 (Embedding) (None, 50, 32) 160000
conv1d_1 (Conv1D) (None, 50, 32) 3104
max_pooling1d_1 (MaxPooling (None, 25, 32) 0
1D)
bidirectional_1 (Bidirectio (None, 64) 16640
nal)
dropout_1 (Dropout) (None, 64) 0
dense_1 (Dense) (None, 3) 195
=================================================================
Total params: 179,939
Trainable params: 179,939
Non-trainable params: 0
_________________________________________________________________
None
Epoch 1/20
1824/1824 [==============================] - 79s 38ms/step - loss: 0.9588 - accuracy: 0.5267 - precision: 0.6060 - recall: 0.2910 - val_loss: 0.8209 - val_accuracy: 0.6337 - val_precision: 0.6852 - val_recall: 0.5150
Epoch 2/20
1824/1824 [==============================] - 92s 50ms/step - loss: 0.7107 - accuracy: 0.6857 - precision: 0.7228 - recall: 0.6250 - val_loss: 0.6318 - val_accuracy: 0.7484 - val_precision: 0.7751 - val_recall: 0.7088
Epoch 3/20
1824/1824 [==============================] - 114s 62ms/step - loss: 0.5263 - accuracy: 0.8051 - precision: 0.8205 - recall: 0.7828 - val_loss: 0.4416 - val_accuracy: 0.8500 - val_precision: 0.8590 - val_recall: 0.8379
Epoch 4/20
1824/1824 [==============================] - 116s 64ms/step - loss: 0.4142 - accuracy: 0.8643 - precision: 0.8726 - recall: 0.8541 - val_loss: 0.4122 - val_accuracy: 0.8529 - val_precision: 0.8587 - val_recall: 0.8460
Epoch 5/20
1824/1824 [==============================] - 96s 53ms/step - loss: 0.3548 - accuracy: 0.8903 - precision: 0.8954 - recall: 0.8841 - val_loss: 0.3456 - val_accuracy: 0.8887 - val_precision: 0.8924 - val_recall: 0.8831
Epoch 6/20
1824/1824 [==============================] - 81s 44ms/step - loss: 0.3258 - accuracy: 0.9019 - precision: 0.9065 - recall: 0.8968 - val_loss: 0.3082 - val_accuracy: 0.9073 - val_precision: 0.9105 - val_recall: 0.9038
Epoch 7/20
1824/1824 [==============================] - 91s 50ms/step - loss: 0.3045 - accuracy: 0.9104 - precision: 0.9145 - recall: 0.9064 - val_loss: 0.3317 - val_accuracy: 0.8969 - val_precision: 0.8999 - val_recall: 0.8932
Epoch 8/20
1824/1824 [==============================] - 78s 43ms/step - loss: 0.2914 - accuracy: 0.9147 - precision: 0.9186 - recall: 0.9107 - val_loss: 0.3285 - val_accuracy: 0.8969 - val_precision: 0.9003 - val_recall: 0.8933
Epoch 9/20
1824/1824 [==============================] - 66s 36ms/step - loss: 0.2810 - accuracy: 0.9188 - precision: 0.9224 - recall: 0.9149 - val_loss: 0.2904 - val_accuracy: 0.9145 - val_precision: 0.9175 - val_recall: 0.9117
Epoch 10/20
1824/1824 [==============================] - 63s 35ms/step - loss: 0.2744 - accuracy: 0.9220 - precision: 0.9255 - recall: 0.9179 - val_loss: 0.2843 - val_accuracy: 0.9158 - val_precision: 0.9186 - val_recall: 0.9125
Epoch 11/20
1824/1824 [==============================] - 63s 35ms/step - loss: 0.2680 - accuracy: 0.9239 - precision: 0.9274 - recall: 0.9201 - val_loss: 0.2808 - val_accuracy: 0.9175 - val_precision: 0.9207 - val_recall: 0.9145
Epoch 12/20
1824/1824 [==============================] - 64s 35ms/step - loss: 0.2625 - accuracy: 0.9253 - precision: 0.9287 - recall: 0.9216 - val_loss: 0.2960 - val_accuracy: 0.9128 - val_precision: 0.9161 - val_recall: 0.9102
Epoch 13/20
1824/1824 [==============================] - 64s 35ms/step - loss: 0.2571 - accuracy: 0.9272 - precision: 0.9306 - recall: 0.9240 - val_loss: 0.2775 - val_accuracy: 0.9197 - val_precision: 0.9225 - val_recall: 0.9159
Epoch 14/20
1824/1824 [==============================] - 59s 32ms/step - loss: 0.2553 - accuracy: 0.9281 - precision: 0.9315 - recall: 0.9245 - val_loss: 0.2865 - val_accuracy: 0.9165 - val_precision: 0.9195 - val_recall: 0.9133
Epoch 15/20
1824/1824 [==============================] - 58s 32ms/step - loss: 0.2522 - accuracy: 0.9288 - precision: 0.9326 - recall: 0.9254 - val_loss: 0.2768 - val_accuracy: 0.9202 - val_precision: 0.9235 - val_recall: 0.9168
Epoch 16/20
1824/1824 [==============================] - 57s 31ms/step - loss: 0.2493 - accuracy: 0.9300 - precision: 0.9336 - recall: 0.9268 - val_loss: 0.2761 - val_accuracy: 0.9200 - val_precision: 0.9224 - val_recall: 0.9173
Epoch 17/20
1824/1824 [==============================] - 57s 31ms/step - loss: 0.2464 - accuracy: 0.9313 - precision: 0.9347 - recall: 0.9278 - val_loss: 0.2927 - val_accuracy: 0.9106 - val_precision: 0.9139 - val_recall: 0.9065
Epoch 18/20
1824/1824 [==============================] - 57s 31ms/step - loss: 0.2452 - accuracy: 0.9319 - precision: 0.9353 - recall: 0.9285 - val_loss: 0.2765 - val_accuracy: 0.9193 - val_precision: 0.9221 - val_recall: 0.9157
Epoch 19/20
1824/1824 [==============================] - 57s 31ms/step - loss: 0.2427 - accuracy: 0.9325 - precision: 0.9356 - recall: 0.9291 - val_loss: 0.2766 - val_accuracy: 0.9199 - val_precision: 0.9223 - val_recall: 0.9163
Epoch 20/20
1824/1824 [==============================] - 57s 31ms/step - loss: 0.2402 - accuracy: 0.9331 - precision: 0.9365 - recall: 0.9299 - val_loss: 0.2781 - val_accuracy: 0.9193 - val_precision: 0.9221 - val_recall: 0.9161
loss, accuracy, precision, recall = model.evaluate(X_test, y_test, verbose=0)
# Print metrics
print('')
print('Accuracy : {:.4f}'.format(accuracy))
print('Precision : {:.4f}'.format(precision))
print('Recall : {:.4f}'.format(recall))
print('F1 Score : {:.4f}'.format(f1_score(precision, recall)))
Accuracy : 0.9122 Precision : 0.9153 Recall : 0.9092 F1 Score : 0.9123
def plot_training_hist(history):
'''Function to plot history for accuracy and loss'''
fig, ax = plt.subplots(1, 2, figsize=(10,4))
# first plot
ax[0].plot(history.history['accuracy'])
ax[0].plot(history.history['val_accuracy'])
ax[0].set_title('Model Accuracy')
ax[0].set_xlabel('epoch')
ax[0].set_ylabel('accuracy')
ax[0].legend(['train', 'validation'], loc='best')
# second plot
ax[1].plot(history.history['loss'])
ax[1].plot(history.history['val_loss'])
ax[1].set_title('Model Loss')
ax[1].set_xlabel('epoch')
ax[1].set_ylabel('loss')
ax[1].legend(['train', 'validation'], loc='best')
plot_training_hist(history)
from sklearn.metrics import confusion_matrix
def plot_confusion_matrix(model, X_test, y_test):
'''Function to plot confusion matrix for the passed model and the data'''
sentiment_classes = ['Negative', 'Neutral', 'Positive']
# use model to do the prediction
y_pred = model.predict(X_test)
# compute confusion matrix
cm = confusion_matrix(np.argmax(np.array(y_test),axis=1), np.argmax(y_pred, axis=1))
# plot confusion matrix
plt.figure(figsize=(8,6))
sns.heatmap(cm, cmap=plt.cm.Blues, annot=True, fmt='d',
xticklabels=sentiment_classes,
yticklabels=sentiment_classes)
plt.title('Confusion matrix', fontsize=16)
plt.xlabel('Actual label', fontsize=12)
plt.ylabel('Predicted label', fontsize=12)
plot_confusion_matrix(model, X_test, y_test)
1140/1140 [==============================] - 13s 7ms/step
model.save('best_model.h5')
print('Best model saved')
Best model saved
from keras.models import load_model
from keras.preprocessing.text import Tokenizer
# Load model
model = load_model('best_model.h5')
def predict_class(text):
'''Function to predict sentiment class of the passed text'''
sentiment_classes = ['Negative', 'Neutral', 'Positive']
max_len=50
# Transforms text to a sequence of integers using a tokenizer object
xt = tokenizer.texts_to_sequences(text)
# Pad sequences to the same length
xt = pad_sequences(xt, padding='post', maxlen=max_len)
# Do the prediction using the loaded model
yt = model.predict(xt).argmax(axis=1)
print(yt)
# Print the predicted sentiment
print('The predicted sentiment is', sentiment_classes[yt[0]])
predict_class(['modi is terrible politician'])
1/1 [==============================] - 1s 1s/step [0] The predicted sentiment is Negative
import snscrape.modules.twitter as sntwitter
# define the Twitter user to scrape
user = 'AITCofficial'
# create a list to hold our tweets
tweets = []
# use snscrape to scrape the tweets
for tweet in sntwitter.TwitterSearchScraper(f'from:{user}').get_items():
tweets.append({
'content': tweet.content,
'date': tweet.date,
'likes': tweet.likeCount,
'retweets': tweet.retweetCount,
'replies': tweet.replyCount
})
# print the tweets
for tweet in tweets:
print(tweet)